#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <fstream>
#include "rand.h"
#define min(a, b) (((a) < (b))? (a) : (b)  )
#define max(a, b) (((a) > (b))? (a) : (b)  )

//this random a number have _d_num digits by random each digit
float new_uniform(int _d_num)
{
	float base=1;
	float sum=0; 
	for (int i=0; i<_d_num; i++)
	{
		int digit=uniform(0, 10);
		if (digit==10) digit=9;
		sum+=base*digit;
		base*=10;
	}
	return sum;
}

float new_uniform(float _min, float _max)
{
	float ran_base=9999999;
	float ran=new_uniform(7);
	return ran/ran_base*(_max-_min)+_min;
}

/*****************************************************************
this function generates a spatio-temporal dataset 
para:
ofname: the output file name
N: cardinality  "so phan tu"
U: spatial universe
V: velocity space is [-V, V]
d: density
_maxDeltaV: maximum velocity change per update
a: agility 
T: number of timestamps 
*****************************************************************/
struct MovingObject
{
	int id;
	float mbr[4];
	float vbr[4];
	float ref;		//reference time
};

void MovingObjectDataset(char *_ofname, int _N, float _U, float _V, float _d, float _maxDeltaV,
						   float _a, int _T)
{
	int i,j,k;
	float len=sqrt(_d/_N);
	
	MovingObject *object=new MovingObject[_N];
	FILE *ofp=fopen(_ofname, "w");

	printf("generating timestamp 0...\n");
	for (int i=0; i<_N; i++)
	{
		for (int j=0; j<2; j++)
		{
			object[i].id=i;
			object[i].ref=0;
			object[i].mbr[2*j]=new_uniform(0, _U-len);
			object[i].mbr[2*j+1]=object[i].mbr[2*j]+len;

			float die=new_uniform(0, 1);
			float sign=1;
			if (die<0.5) sign=-1; 
			object[i].vbr[2*j]=object[i].vbr[2*j+1]=sign*new_uniform(0, _V);
		}
		fprintf(ofp, "%d %f %f %f %f %f %f %f %f %d\n", object[i].id, object[i].mbr[0],
			object[i].mbr[1], object[i].mbr[2], object[i].mbr[3], object[i].vbr[0],
			object[i].vbr[1], object[i].vbr[2], object[i].vbr[3], 0);
	}

	int changeNum=_a*_N;
	//int changeNum=_N;
	int *changeId=new int[changeNum];

	for (i=1; i<=_T; i++)
	{
		printf("generating timestamp %d...\n", i);
		//first generate a set of ids for issuing changes --------
		for (int j=0; j<changeNum; j++)
		{
			if (_a==1)
				changeId[j]=j;
			else 
			{
		//	/*
				changeId[j]=-1;
				int genId;
				bool run=true;
				while (run)
				{
					run=false;
					genId=new_uniform(0, _N+1);
					if (genId>=_N) 
						genId=_N-1;
					for (int k=0; k<j; k++)
					{
						if (changeId[k]==genId)
						{
							run=true; break;
						}
					}
				}
				changeId[j]=genId;
		//	*/
			}
		}
		//for each selected object generate its change -----------
		for (j=0; j<changeNum; j++)
		{
			int updateId=changeId[j];
			//first update its mbr
			for (int k=0; k<2; k++)
			{
				object[updateId].mbr[2*k]+=object[updateId].vbr[2*k]*(i-object[updateId].ref);
				object[updateId].mbr[2*k+1]+=object[updateId].vbr[2*k+1]*(i-object[updateId].ref);
				//check if the mbr falls out of the data space
				if (object[updateId].mbr[2*k]<0 || object[updateId].mbr[2*k+1]>_U)
				{
					float extent=object[updateId].mbr[2*k+1]-object[updateId].mbr[2*k];
					object[updateId].mbr[2*k]=0.5-extent; object[updateId].mbr[2*k+1]=0.5; 
				}
			}
			//then its vbr;
			for (k=0; k<2; k++)
			{
				float deltav=new_uniform(-_maxDeltaV, _maxDeltaV);
				object[updateId].vbr[2*k]+=deltav;
				//check if vbr falls out or the data space
				if (object[updateId].vbr[2*k]<-_V)
					object[updateId].vbr[2*k]+=_V;
				if (object[updateId].vbr[2*k+1]>_V)
					object[updateId].vbr[2*k]-=_V;
					
				object[updateId].vbr[2*k+1]=object[updateId].vbr[2*k];
			}
		
			object[updateId].ref=i;

			fprintf(ofp, "%d %f %f %f %f %f %f %f %f %d\n", object[updateId].id, 
				object[updateId].mbr[0], object[updateId].mbr[1], object[updateId].mbr[2], 
				object[updateId].mbr[3],
				object[updateId].vbr[0], object[updateId].vbr[1], object[updateId].vbr[2], 
				object[updateId].vbr[3], i);
		}
	}
	
	delete []changeId;
	fclose(ofp);
	delete []object;
}


void main()
{
	srand( (unsigned)time( NULL ) );
	//10k updates
	MovingObjectDataset("../DS/data10k.txt", 10000, 10000, 50, 0.5, 5, 1, 0);
	//MovingObjectDataset("../DS/data30k.txt", 30000, 10000, 50, 0.5, 5, 1, 0);
	//MovingObjectDataset("../DS/data50k400T.txt", 50000, 10000, 50, 0.5, 5, 1, 0);
	//MovingObjectDataset("../DS/data70k.txt", 70000, 10000, 50, 0.5, 5, 1, 0);
	//MovingObjectDataset("../DS/data100k.txt", 100000, 10000, 50, 0.5, 5, 1, 0);
	

	/*
	//20k updates
	MovingObjectDataset("../DS/update20k.txt", 10000, 10000, 50, 0.5, 5, 1, 2);
	//30k updates
	MovingObjectDataset("../DS/update30k.txt", 10000, 10000, 50, 0.5, 5, 1, 3);
	//40k updates
	MovingObjectDataset("../DS/update40k.txt", 10000, 10000, 50, 0.5, 5, 1, 4);
	//50k updates
	MovingObjectDataset("../DS/update50k.txt", 10000, 10000, 50, 0.5, 5, 1, 5);
	//60k updates
	MovingObjectDataset("../DS/update60k.txt", 10000, 10000, 50, 0.5, 5, 1, 6);
	//70k updates
	MovingObjectDataset("../DS/update70k.txt", 10000, 10000, 50, 0.5, 5, 1, 7);
	//80k updates
	MovingObjectDataset("../DS/update80k.txt", 10000, 10000, 50, 0.5, 5, 1, 8);
	//90k updates
	MovingObjectDataset("../DS/update90k.txt", 10000, 10000, 50, 0.5, 5, 1, 9);
	//100k updates
	MovingObjectDataset("../DS/update100k.txt", 10000, 10000, 50, 0.5, 5, 1, 10);
	*/


//	MovingObjectDataset("../DS/up3-time4.txt", 10000, 10000, 50, 0.5, 5, 0.01, 10);
//	MovingObjectDataset("../ds/huge-50.txt", 10000, 10000, 10, 0.5, 5, 1.0, 50);
//	MovingObjectDataset("../ds/small.txt", 10000, 10000, 10, 0.5, 5, 1.0, 0);
}